Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add completion settings (Hinterland mode, documentation, suppression) #315

Merged
merged 133 commits into from
Aug 31, 2020

Conversation

krassowski
Copy link
Member

@krassowski krassowski commented Aug 11, 2020

References

Implements #25, closes #314, addresses a long standing feature request by @simon-ritchie in early feedback here (the Hinterland mode).

Code changes

  • store global references/state in LSPExtension to simplify passing stuff around (especially settings)
  • add IFeatureSettings interface to allow isolate access to subset of settings by the features (implemented by LabFeatureSettings)
    • the interface is provisional as I imagine that server/language-specific behaviour would be nice to have (soon!) and for now it only does global stuff. I think that its responsibility will be to merge properties from global settings and language-specific settings (and give the feature whichever it wants).
  • one hard-coded variable less (invocation suppression tokens)

User-facing changes

  • settings now include "features" property
    • display of documentation in the completer can be disabled
    • user can now decide which tokens to ignore
    • continuous hinting (suggestion after typing each key) is now implemented ("Hinterland mode")
  • reused the diagnostics icon for settings
    • it may not be a perfect icon for LSP as a whole, but it seems good enough in this context; I think that most users may not be too familiar with LSP term (even though they may understand what LSP is when they are installing it - unless it comes pre-installed) so giving them a visual hint of the diagnostics icon could be useful (in settings context). Though I think it would be misleading if we did the same on the statusbar...

completion_settings

Backwards-incompatible changes

  • JupyterLabWidgetAdapter constructor was rewritten

Chores

  • linted
  • tested
  • documented
  • changelog entry

@krassowski
Copy link
Member Author

I guess I will have to write tests for that at some point... I guess I will do one for the documentation in completer behaviour tomorrow so that we have at least one test using the settings before we merge this.

"title": "LSP features",
"description": "Client-side features configuration",
"$ref": "#/definitions/features",
"default": {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if the defaults are actually preserved all the way down... it might just be the top-level object. would have to check.

Copy link
Member Author

@krassowski krassowski Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, they seem to be preserved - at least in the sense of being correctly displayed in the settings editor; how else can I specify them? propertiescompletion and then default?

settings: ISettingRegistry.ISettings;

constructor(
public app: JupyterFrontEnd,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This signature is already way too complicated. A more common pattern would be to have a ILanguageServerManager.IOptions which defined all these pieces, and didn't conflate the variable scopes (public/private) in the constructor. Putting this in a (new) tokens.ts will help downstreams understand what the language server manager can provide to them.

The main advantage here is that it is easier to add a property to an unordered set of named arguments rather than getting the perfect ordering of many disparate types.

Copy link
Member Author

@krassowski krassowski Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that there are a few advantages of IOptions pattern, but it does not seem greatly useful for a function which gets called once ever (and with arguments that we do not control). Besides, having IOptions here would just move the problem of relying on a specific ordering of arguments to the activate() function (which already led to a hard-to-debug issue - explicit typing with a list of types is based on that lesson).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

advantages, off the top of my head:

  • easier to document
  • easier to extend
  • better completion
  • more like core

activate is needfully the special case, at the application level, so that things can be replaced.

ILabShell,
IStatusBar
],
activate: (app, ...args) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see what you're doing here, and yes, relying on so many things is onerous. but this seems like a very roundabout way to leverage the type system... and basically guarantees that the signature, and therefore the API, will break the next time this changes. Sure, you're not returning this superobject yet, but this would all but need to be the public API.

my experience has been to minimize the number of external APIs that are actually called inside the manager class, and favor connecting to top-level signals in the extension.ts. this centralizes the potential API surface to the best

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. not returning because activate has void signature
  2. as long as the requires and as [] stay in sync this will not break (ideally there would be a const TOKENS = [...] on type TOKENS = [...] defined in a single line, but I could not get it working)
  3. yes the public API will change the next time we add a token dependency, but is this a bad thing? LSPExtension is only meant to be used in the activate function (can document this). I see that I export it but maybe if I exported an interface with the things that are meant to be passed down, then LSPExtension would no longer be public. On the other hand, I do not feel convenient having the extra interface if I can just have an extension object. The extension class just seems so natural to have (and it to contain app and settings references)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick note: 1 no longer the case in the refactor, this was my limited understanding.

@@ -44,20 +41,11 @@ export class FileEditorAdapter extends JupyterLabWidgetAdapter {
}

constructor(
extension: LSPExtension,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing here: when we start changing constructors, we should just go ahead and adopt an IOptions someplace so the options can grow more organically over time. The closer we can start making <ActivityName>WidgetAdapter invocation to be more regular, I think the happier we'll be...

'completer:invoke-file',
connection_manager
);
super(extension, editor_widget, 'completer:invoke-file');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably collect all of our external command references someplace...

return this.get_obj()[setting];
}

set(setting: string, value: any) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, you could reus the T from above, and always set to a Partial<T>: fewer magic strings, and no looping (outside of here) to set multiple values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand. Could you throw me some example, plese?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll see if i can dig something up. Basically, if you know T, you can ask for setting: keyof T, and then the value can be inferred by type inference. I think lumino has some stuff like that.

) {
this.app = extension.app;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if possible, we should try to keep references to the application itself to a minimum...

) {
this.app = extension.app;
this.rendermime_registry = extension.rendermime_registry;
this.connection_manager = extension.connection_manager;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, many of these things could just become dumb getters that wrap this.extension.X (or this.language_server_manager.X)

this.document_connected = new Signal(this);
this.invoke_command = invoke;
this.adapters = new Map();
this.status_message = new StatusMessage();
this.connection_manager = connection_manager;
this.completion_settings = new LabFeatureSettings(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like sort of a weird place to land this...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see https://github.com/krassowski/jupyterlab-lsp/pull/315/files#diff-768f6aea86149c0aa51e05111ea0968cR160. I don't like it either but seems the best thing to maintain separation of lab-specific and codemirror-specific code without major refactoring. I mean it should probably go like Feature = LabFeature + CMFeature + Settings (which are relevant to both) - I can work on it in a subsequent PR.

@krassowski
Copy link
Member Author

Thanks for the feedback! I will try to adress the points tomorrow.

@krassowski krassowski added this to the 1.2 milestone Aug 12, 2020
@krassowski
Copy link
Member Author

@bollwyvl Most of the issues you point out are can be solved by #316. However, it is a substantial rewrite and I would like to publish this version tomorrow to get up to speed with Lab 2.2.

@bollwyvl
Copy link
Collaborator

this is also a substantial rewrite... i mean, whatever, but it's going to get pretty headachey to start bringing the kernel branch back up to snuff.

krassowski and others added 26 commits August 28, 2020 23:15
Implement completer icons and add a stub for themes system
Fix statusbar message pooling data from all editors
Make code extractors and replacements modular
Try to use the most recent build of tectonic again
@krassowski krassowski merged commit a65019a into master Aug 31, 2020
@krassowski krassowski modified the milestones: 1.2, 2.0 Sep 4, 2020
@krassowski krassowski deleted the configurable-competer branch December 13, 2020 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow to disable the documentation preview in completer
2 participants